package act.ein3c.in3;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

import javax.swing.Timer;



public class Sender implements Runnable {
	private int waitingForAck = 0;
	private int duplicate = 0;
	private int timesTripleDuplicate = 0;
//	private int toSend = 0;
	private ObjectOutputStream output;
	private BufferedReader input;
	private Socket connectionSocket;
	private ArrayList<Packet> sendingWindow	= new ArrayList<Packet>();
	private ArrayList<Packet> data = new ArrayList<Packet>();

//	private ArrayList<Integer> waitForAck = new ArrayList<Integer>();
	private ArrayList<Integer> ackList = new ArrayList<Integer>();	
	
	
	
	private boolean done = false;
	private boolean end = false;
	
	private final int maxSendingWindow = 4;
	private Timer ticker;

	public Sender(){
		ticker = new Timer(1000, new AL(this));
	}
	
	public static void main(String[] args){
		Sender s = new Sender();
		s.run();
	}

	public void run() {
		makeConnection();
		getInput();
		makeData();

		startTimer();
		
		while (ticker.isRunning() && !done && !end) {
			sendData();
		}
		if(end){
			System.err.println("Sender: Packet missing");
		}

	}

	private void makeData() {
		addData("test1");
		addData("test2");
		addData("test3");
		addData("test4");
		addData("test5");
		addData("test6");
	}
	


	public void makeConnection() {
		try {
			connectionSocket = new Socket("localhost", 9090);
			output = new ObjectOutputStream(connectionSocket.getOutputStream());
		} catch (UnknownHostException e) {
			System.err.println("Sender: Host not found");
			e.printStackTrace();
		} catch (IOException e) {
			System.err.println("Sender: No connection found");
			e.printStackTrace();
			
		}
	}

	public void setData(ArrayList<Packet> newData) {
		data = newData;
	}

	public void addData(String text) {
		data.add(new Packet(data.size(), text));
	}

	private void send(){
		if( checkEnd() ){
			return;
		}

		try {
			if (!data.isEmpty()) {
				Packet nextPacket = data.get(0);
				sendingWindow.add(nextPacket);
				output.writeObject(nextPacket);
				System.out.println("Sender: sending packet sqnumber: "
						+ nextPacket.getNumber());

				data.remove(nextPacket);
				startTimer();
			}
			waiting(100000);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void sendData() {
		send();
		checkEnd();
		listening();

	}

	private boolean checkEnd() {
		if (data.isEmpty() && sendingWindow.isEmpty()) {
			System.err.println("Sender: Geen data te verzenden");
			stopTimer();
			done = true;
		}
		return done;
	}

	private void getInput() {
		try {
			input = new BufferedReader(new InputStreamReader(
					connectionSocket.getInputStream()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void listening() {
		if(done){
			return;
		}
		boolean acked = false;
		
		if (ticker.isRunning() && !acked && duplicate < 3) {
			
			try {
				String message = "";
				if( input.ready() )
					message = input.readLine();
				
				if (message.length() >= 3) 
					acked = checkAck(acked, message);
								
			} catch (IOException e) {
				System.err.println("Sender: No input!");
			}
		}
		
		checkForDuplicate();
		
		checkTimer();

	}

	private void checkForDuplicate() {
		if(duplicate >=3){
			System.err.println("Sender: triple duplicate");
			waitingForAck++;
			
			timesTripleDuplicate++;
			if(timesTripleDuplicate > 10){
				end = true;
				return;
			}
			sendData();
			
		}
	}

	private void checkTimer() {
		if(!ticker.isRunning()){
			System.err.println("Sender: timeout");
			waitingForAck++;
			sendData();
		}
	}
	
	private void startTimer(){
		ticker.start();
	}
	
	private void stopTimer(){
		ticker.stop();
	}
	
	public void restartTimer()
	{
		ticker.restart();
	}


	private boolean checkAck(boolean acked, String message) {
		if (message.substring(0, 1).equalsIgnoreCase("a")) {
			if (message.substring(2, 3).equalsIgnoreCase(Integer.toString(waitingForAck))) {
				ackList.add(waitingForAck);
				acked = true;
				waitingForAck++;
				System.out.println("Sender: " + message);
			} else {
				if (message.substring(2, 3).equalsIgnoreCase( Integer.toString(waitingForAck - 1) ) ) {
					duplicate++;
					
					System.out.println("Sender: duplicate ack " + duplicate);
				

				}
			}

		}
		
		return acked;
	}
	
	private boolean sendingWindowNotFull() {
		return sendingWindow.size() < maxSendingWindow;	
	}
	
	public void waiting(int n) {
		long time0, time1;
		time0 = System.currentTimeMillis();
		do {
			time1 = System.currentTimeMillis();
		} while (time1 - time0 < n);
	}
	
	
	private class AL implements ActionListener
	{
		private Sender send;
		
		public AL(Sender send)
		{
			this.send = send;
		}

		@Override
		public void actionPerformed(ActionEvent arg0)
		{
			send.checkTimer();
		}
	}


}


